import {Injectable} from "@angular/core";
import {
  HttpClient,
  HttpErrorResponse,
  HttpEvent,
  HttpHandler, HttpHeaders,
  HttpInterceptor, HttpParams,
  HttpRequest,
  HttpResponse
} from "@angular/common/http";
import {catchError, finalize, Observable, of, OperatorFunction, tap, throwError} from "rxjs";
import {InterceptorConfig} from "@core/interceptor/InterceptorConfig";
import {InterceptorHttpParams} from "@core/interceptor/InterceptorHttpParams";
import {Constants} from "@app/shared/constant";
import {AuthService} from "@core/auth/AuthService";

let tmpHeaders: HttpHeaders | undefined = undefined;

@Injectable(
  {providedIn: "root"}
)
export class GlobalHttpInterceptor implements HttpInterceptor {
  private internalUrlPrefixes = [
    '/api/auth/token',
    '/api/rpc'
  ];

  constructor(public http: HttpClient) {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.url.startsWith('/api/')) {
      const config = this.getInterceptorConfig(req);
      this.updateLoadingState(config, true);
      let observable$: Observable<HttpEvent<any>>;
      //是否需要token
      if (this.isBaseToken(req.url)) {
        if (AuthService.token === null) {
          observable$ = this.handleError(req, next, new HttpErrorResponse({error: 'UnAuthenticated'}));
        } else
          observable$ = this.setToken(req, next);
      } else {
        //不需要token认证的方法
        observable$ = this.handleResponse(req, next);
      }
      return observable$.pipe(
        finalize(() => {
            this.updateLoadingState(config, false);
          }
        )
      );
    } else
      return next.handle(req);
  }

  //在req中设置token
  private setToken(req: HttpRequest<any>,
                   next: HttpHandler): Observable<HttpEvent<any>> {
    if (tmpHeaders === undefined) {
      tmpHeaders = new HttpHeaders({
        [Constants.X_Authorization]: [Constants.prefixAuthorization, AuthService.token].join('')
      });
    }
    const newReq = req.clone({
      headers: tmpHeaders
    });
    return next.handle(newReq).pipe(
      catchError(err => {
          return throwError(() => err)
        }
      )
    )
  }

  //请求处理方法
  private handleResponse(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError(err => {
        if (err instanceof HttpErrorResponse) {
          if (err.status === 401) {
            // 401 handled in auth.interceptor
            return throwError(() => err);
          } else if (err.status === 403) {
            return this.handleError(req, next, err);
          } else {
            return this.handleError(req, next, err);
          }
        } else {
          return this.handleError(req, next, err);
        }
      })
    );
  }

  //刷新token方法

  //在请求执行完毕后执行的方法
  private updateLoadingState(config: InterceptorConfig, loading: boolean) {
    if (!config.ignoreLoading) {
    }
  }

  private handleError(req: HttpRequest<any>, next: HttpHandler,
                      error: HttpErrorResponse): Observable<HttpEvent<any>> {
    return of(new HttpResponse({
      body: error.error,
      headers: error.headers,
      status: error.status,
      statusText: error.statusText,
      url: error.url as string
    }));
  }

  private getInterceptorConfig(req: HttpRequest<any>): InterceptorConfig {
    let config: InterceptorConfig;
    if (req.params && req.params instanceof InterceptorHttpParams) {
      config = (req.params as InterceptorHttpParams).interceptorConfig;
    } else {
      config = new InterceptorConfig(false, false);
    }
    if (this.isInternalUrlPrefix(req.url)) {
      config.ignoreLoading = true;
    }
    return config;
  }

  private isInternalUrlPrefix(url: string): boolean {
    for (const index in this.internalUrlPrefixes) {
      if (url.startsWith(this.internalUrlPrefixes[index])) {
        return true;
      }
    }
    return false;
  }

  private isBaseToken(url: string) {
    let baseToken: boolean = url.startsWith('/api/');
    const noAuthMap: { [key: string]: string } = Constants.noAuthMap;
    for (let index in noAuthMap) {
      if (url.startsWith(noAuthMap[index])) {
        baseToken = false;
        break;
      } else
        baseToken = true;
    }
    return baseToken
  }
}
